package org.cloud.dimension.filter;

import static org.cloud.constant.CoreConstant.IP_BIND_ERROR;
import static org.cloud.constant.CoreConstant.USER_LOCK_IP_MAP;
import static org.cloud.constant.LoginTypeConstant.LoginTypeEnum.LOGIN_BY_ADMIN_USER;

import java.util.List;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
import org.cloud.context.RequestContext;
import org.cloud.context.RequestContextManager;
import org.cloud.core.redis.RedisUtil;
import org.cloud.entity.LoginUserDetails;
import org.cloud.exception.BusinessException;
import org.cloud.feign.utils.FeignUtil;
import org.cloud.utils.HttpServletUtil;
import org.cloud.utils.IPUtil;
import org.jetbrains.annotations.NotNull;
import org.springframework.http.HttpStatus;
import org.springframework.web.filter.OncePerRequestFilter;

/**
 * 安全过滤器，获取当前用户的信息，存储在上下文中。缓存到redis缓存中，通过网关登录后获取信息，这个过滤器只做基础的用户信息的上下文的过滤。 及Url是否需要登录的校验，其它的权限过滤将下发到各个应用中去做处理。因为不同的模块的权限管控是不同的。
 */
public class SecurityFilter extends OncePerRequestFilter {


    private final Boolean ipLoginLockFlag;

    //security的鉴权排除列表
    private final List<String> excludedAuthPages;
    private final List<String> excludedCheckLoginBindApi;

    public SecurityFilter(List<String> excludedAuthPages, List<String> excludedCheckLoginBindApi, Boolean ipLoginLockFlag) {
        this.excludedAuthPages = excludedAuthPages;
        this.excludedAuthPages.add("/v2/api-docs");
        this.excludedAuthPages.add("/inner/**/*");
        this.excludedAuthPages.add("/user/verify/generate/*");
        this.excludedAuthPages.add("/user/mfa/**");
        this.excludedAuthPages.add("/app/userRef/ipLoginLockFlagOpen");
        this.excludedAuthPages.add("/app/userRef/getCurrentIp");
        this.excludedCheckLoginBindApi = excludedCheckLoginBindApi;
        this.excludedCheckLoginBindApi.add("/app/userRef/isBindLoginIp");
        this.excludedCheckLoginBindApi.add("/app/userRef/ipLoginLockFlagOpen");
        this.excludedCheckLoginBindApi.add("/app/userRef/changeLoginIp");
        this.excludedCheckLoginBindApi.add("/user/menu/getMenus");
        this.excludedCheckLoginBindApi.add("/app/userRef/getCurrentIp");

        this.ipLoginLockFlag = ipLoginLockFlag;
    }

    @SneakyThrows
    @Override
    protected void doFilterInternal(@NotNull HttpServletRequest httpServletRequest, @NotNull HttpServletResponse httpServletResponse,
        @NotNull FilterChain filterChain) {
        // 先设置上下文信息
        final LoginUserDetails user = FeignUtil.single().getLoginUser();
        RequestContext requestContext = new RequestContext();
        requestContext.setHttpServletRequest(httpServletRequest);
        requestContext.setHttpServletResponse(httpServletResponse);
        if (user != null) {
            requestContext.setUser(user);
        }
        RequestContextManager.single().setRequestContext(requestContext);
        // 对权限进行一些校验
        boolean isExcludeUri = HttpServletUtil.single().isExcludeUri(httpServletRequest, excludedAuthPages);
        if (isExcludeUri) {
            RequestContextManager.single().setRequestContext(requestContext);
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }
        // 增加上下文的user的处理
        if (user != null) {
            if (!checkIpLoginBind(user, httpServletRequest)) {
                HttpServletUtil.single().handlerBusinessException(new BusinessException(IP_BIND_ERROR, HttpStatus.BAD_REQUEST), httpServletResponse);
                return;
            }
        }
        filterChain.doFilter(httpServletRequest, httpServletResponse);


    }

    /**
     * 校验ip绑定是否正确
     *
     * @param user
     * @param request
     * @return
     */
    private Boolean checkIpLoginBind(LoginUserDetails user, HttpServletRequest request) {
        if (!ipLoginLockFlag) {
            return true;
        }
        if (HttpServletUtil.single().isExcludeUri(request, excludedCheckLoginBindApi)) {
            return true;
        }

        if (LOGIN_BY_ADMIN_USER.userType.equals(user.getUserType())) {
            return IPUtil.single().getIpAddress(request).equalsIgnoreCase(RedisUtil.single().hashGet(USER_LOCK_IP_MAP, user.getId().toString()));
        }
        return true;
    }
}
